package top.heue.utils.log

import top.heue.utils.log.base.IBaseHandler
import top.heue.utils.log.base.IBasePrinter
import top.heue.utils.log.base.ILRuntime
import top.heue.utils.log.handler.StringHandler
import top.heue.utils.log.handler.ThrowableHandler
import kotlin.collections.mutableMapOf

object L : IL {
    /**
     * 自动标签控制与哨兵标记
     */
    private object AutoTag

    override var loggable = true
    override var logLevel: LogLevel = LogLevel.VERBOSE
    var enableAutoTag: Boolean = true

    init {
        TaskPool.apply {
            add(StringHandler())
            add(ThrowableHandler())
        }
    }

    override var runtime: ILRuntime? = null

    override fun handle(logLevel: LogLevel, tag: Any, vararg any: Any?) {
        if (!loggable) return
        if (logLevel.level < this.logLevel.level) return
        var ret = any
        val t = when {
            tag === AutoTag -> {
                if (enableAutoTag) {
                    val (clazz, method) = current
                    "$clazz.$method"
                } else {
                    "L"
                }
            }

            tag is Pair<*, *> -> {
                ret = arrayOf(tag.second.toString(), *ret)
                tag.first.toString()
            }

            else -> tag.toString()
        }
        val block = {
            TaskPool.execute(logLevel, t, *ret)
        }
        runtime?.run(block) ?: block()
    }

    override fun addHandler(handler: IBaseHandler<*>) = TaskPool.add(handler)

    override fun addPrinter(printer: IBasePrinter) = TaskPool.add(printer)

    fun v(vararg any: Any?) = handle(LogLevel.VERBOSE, AutoTag, *any)

    fun v(tag: String, any: Any?) = handle(LogLevel.VERBOSE, tag, any)
    fun v(tag: String, vararg any: Any?) =
        if (any.isEmpty()) handle(LogLevel.VERBOSE, AutoTag, tag) else handle(LogLevel.VERBOSE, tag, *any)

    fun d(vararg any: Any?) = handle(LogLevel.DEBUG, AutoTag, *any)

    fun d(tag: String, any: Any?) = handle(LogLevel.DEBUG, tag, any)
    fun d(tag: String, vararg any: Any?) =
        if (any.isEmpty()) handle(LogLevel.DEBUG, AutoTag, tag) else handle(LogLevel.DEBUG, tag, *any)

    fun i(vararg any: Any?) = handle(LogLevel.INFO, AutoTag, *any)

    fun i(tag: String, any: Any?) = handle(LogLevel.INFO, tag, any)
    fun i(tag: String, vararg any: Any?) =
        if (any.isEmpty()) handle(LogLevel.INFO, AutoTag, tag) else handle(LogLevel.INFO, tag, *any)

    fun w(vararg any: Any?) = handle(LogLevel.WARN, AutoTag, *any)

    fun w(tag: String, any: Any?) = handle(LogLevel.WARN, tag, any)
    fun w(tag: String, vararg any: Any?) =
        if (any.isEmpty()) handle(LogLevel.WARN, AutoTag, tag) else handle(LogLevel.WARN, tag, *any)

    fun e(vararg any: Any?) = handle(LogLevel.ERROR, AutoTag, *any)

    fun e(tag: String, any: Any?) = handle(LogLevel.ERROR, tag, any)
    fun e(tag: String, vararg any: Any?) =
        if (any.isEmpty()) handle(LogLevel.ERROR, AutoTag, tag) else handle(LogLevel.ERROR, tag, *any)


    override val Throwable.string: String get() = this.stackTraceToString()

    private val apartMap: MutableMap<String, Long> = mutableMapOf()

    /**执行时间计算，将返回两次调用时间差*/
    fun apart(tag: Any = 0, isRemove: Boolean = true) = run {
        var result: Long = 0
        val apartTag = tag.toString()
        if (apartMap[apartTag] == null) {
            apartMap[apartTag] = System.currentTimeMillis()
        } else {
            val c = System.currentTimeMillis()
            result = c - apartMap[apartTag]!!
            if (isRemove) apartMap.remove(apartTag)
            else apartMap[apartTag] = c
        }
        result
    }

    override val current
        get() = run {
            val frames = Throwable().stackTrace
            var clazz = ""
            var method = ""
            for (el in frames) {
                val className = el.className
                if (className.contains("top.heue.utils.log")) continue
                clazz = className.substringAfterLast('.')
                method = el.methodName
                break
            }
            clazz to method
        }

    override fun catchError(threadHandler: (thread: Any?, throwable: Throwable) -> Unit) =
        apply {
            top.heue.utils.log.platform.setUncaughtExceptionHandler(threadHandler)
        }
}
